package cn.tedu.blog.server.service.impl;



import cn.tedu.blog.server.exception.CoolSharkServiceException;
import cn.tedu.blog.server.exception.ServiceException;
import cn.tedu.blog.server.mapper.MenuMapper;
import cn.tedu.blog.server.pojo.dto.MenuAddNewDTO;
import cn.tedu.blog.server.pojo.entity.Menu;
import cn.tedu.blog.server.pojo.entity.MenuTreeEntity;
import cn.tedu.blog.server.pojo.vo.MenuListVO;
import cn.tedu.blog.server.pojo.vo.MenusTreeVO;
import cn.tedu.blog.server.service.IMenuService;
import cn.tedu.blog.server.utils.DateUtils;
import cn.tedu.blog.server.utils.ResponseCode;
import cn.tedu.blog.server.utils.ServiceCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author: 雷茂林
 * @Date:2022/7/17 10:02
 * @Description: 权限管理-业务逻辑实现类
 */

@Slf4j
@Service
public class MenuServiceImpl implements IMenuService {

    @Autowired
    private MenuMapper menuMapper;


    @Autowired
    private RedisTemplate redisTemplate;


    // 开发规范标准：为了降低Redis的Key拼写错误的风险，我们都会定义常量
    public static final String Menu_TREE_KEY = "menu_tree";


    @Override
    public void createMenu(MenuAddNewDTO menuAddNewDTO) {
        log.debug("准备开始处理创建菜单的业务，参数：{}", menuAddNewDTO);
        boolean isTrueCreateSort = true;
        if (menuAddNewDTO != null) {
            if (menuMapper.isValueExistenceById(0L, menuAddNewDTO.getValue()) > 0) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "权限地址【" + menuAddNewDTO.getValue() + "】已被使用！");
            }
            if (menuMapper.isNameExistenceById(0L, menuAddNewDTO.getName()) > 0) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "权限名称【" + menuAddNewDTO.getName() + "】已被使用！");
            }
        }

        Menu menu = new Menu();
        BeanUtils.copyProperties(menuAddNewDTO, menu);
        //补全信息
        if (menu.getParentId() == null || menu.getParentId() == 0) {
            menu.setParentId(0L);
            menu.setParent(1);
            menu.setDepth(1);
        } else {
            menu.setParent(0);
            if (menuMapper.isParentIdByCurrentId(menu.getParentId()) == 0) {
                menu.setDepth(2);
            } else {
                menu.setDepth(3);
            }
        }

        // 补全排序
        if (menu.getSort() == null) {
            menu.setSort(0);
            isTrueCreateSort = false;
        }
        if (menu.getSort() == 0) {
            if (menu.getParentId() != null && menu.getParentId() != 0) {
                menu.setSort(menuMapper.getMenuById(menu.getParentId()).getSort());
                isTrueCreateSort = true;
            }
        }

        LocalDateTime now = LocalDateTime.now();
        menu.setGmtCreate(now);
        menu.setGmtModified(now);
        log.debug("即将向数据库写入创建的菜单，参数：{}", menu);
        if (menuMapper.createMenu(menu) <= 0) {
            throw new ServiceException(ServiceCode.ERR_INSERT, "创建菜单失败，请稍后再试！");
        }

        // 添加排序
        if (!isTrueCreateSort) {
            if (menu.getId() >= 2147483647) {
                throw new ServiceException(ServiceCode.ERR_UNKNOWN, "请指定排序参数，如需默认请联系管理员");
            } else {
                MenuAddNewDTO menuAddNewDTO1 = new MenuAddNewDTO();
                menuAddNewDTO1.setId(menu.getId());
                menuAddNewDTO1.setSort(menu.getId().intValue());
                menuMapper.updateById(menuAddNewDTO1);
            }
        }

        // 删除redis中的key
        deleteRedisByKey();
    }


    @Override
    public void deleteById(Long id) {
        log.debug("准备开始处理删除菜单的业务，参数：{}", id);
        if (menuMapper.isMenuById(id) <= 0) {
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, "菜单不存在");
        }

        if (menuMapper.isSonMenuById(id) > 0) {
            MenuListVO menuListVO = menuMapper.getMenuById(id);
            throw new ServiceException(ServiceCode.ERR_DELETE, "【" + menuListVO.getName() + "】为节点菜单，禁止直接删除");
        }

        log.debug("即将移除菜单ID为：【{}】的菜单", id);
        if (menuMapper.deleteById(id) <= 0) {
            throw new ServiceException(ServiceCode.ERR_DELETE, "移除菜单失败，请稍后再试！");
        }

        // 查询当前权限的父级权限id
        Long parentId = menuMapper.isParentIdByCurrentId(id);
        if (menuMapper.isSonMenuById(parentId) <= 0) {
            // 如果父级id没有子级了，则更改父级权限状态为0(不是父级)
            MenuAddNewDTO menuAddNewDTO = new MenuAddNewDTO();
            menuAddNewDTO.setId(parentId);
            menuAddNewDTO.setParent(0);
            menuAddNewDTO.setGmtModified(LocalDateTime.now());
            menuMapper.updateById(menuAddNewDTO);
        }

        // 删除redis中的key
        deleteRedisByKey();
    }


    @Override
    public void deleteByIds(List<Long> ids) {
        log.debug("准备开始处理【批量删除菜单】的业务，参数：{}", ids);

        for (Long id : ids) {
            if (menuMapper.isSonMenuById(id) > 0) {
                MenuAddNewDTO[] menuAddNewDTOS = menuMapper.getSubPermissionsWhole(id);
                Long[] arr = new Long[menuAddNewDTOS.length];
                for (int i = 0; i < menuAddNewDTOS.length; i++) {
                    arr[i] = menuAddNewDTOS[i].getId();
                }


                int a = 0;
                for (int i = 0; i < arr.length; i++) {
                    for (int j = 0; j < ids.size() - 1; j++) {
                        if (arr[i] == (long) ids.get(i + 1)) {
                            a++;
                            break;
                        }
                    }
                }
                if (a != arr.length) {
                    MenuListVO menuListVO = menuMapper.getMenuById(id);
                    throw new ServiceException(ServiceCode.ERR_DELETE, "当前操作中【" + menuListVO.getName() + "】为节点菜单，禁止直接删除");
                }
                log.debug("发现【" + id + "】为节点菜单，其子元素全部包含在批量删除中，满足批量删除条件，删除中...");
            }
        }

        if (menuMapper.deleteByIds(ids) <= 0) {
            throw new ServiceException(ServiceCode.ERR_DELETE, "批量移除权限失败，请稍后再试！");
        }

        // 删除redis中的key
        deleteRedisByKey();
    }


    /**
     * 修改：修改菜单
     * @param menuAddNewDTO
     */
    @Override
    public void updateById(MenuAddNewDTO menuAddNewDTO) {
        log.debug("准备开始处理修改菜单的业务，参数：{}", menuAddNewDTO);
        if (menuAddNewDTO.getValue() != null) {
            if (menuMapper.isValueExistenceById(menuAddNewDTO.getId(), menuAddNewDTO.getValue()) > 0) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "菜单地址【" + menuAddNewDTO.getValue() + "】已被使用！");
            }
            if (menuMapper.isNameExistenceById(menuAddNewDTO.getId(), menuAddNewDTO.getName()) > 0) {
                throw new ServiceException(ServiceCode.ERR_CONFLICT, "权限名称【" + menuAddNewDTO.getName() + "】已被使用！");
            }
        }

        // 获取当前权限修改前的信息
        MenuListVO menuListVO = menuMapper.getMenuById(menuAddNewDTO.getId());

        if (menuListVO.getParentId() == null) {
            menuAddNewDTO.setParentId(0L);
        }
        if (menuAddNewDTO.getParentId() != menuListVO.getParentId()) {
            isViolation(menuAddNewDTO);
        }

        menuAddNewDTO.setGmtModified(LocalDateTime.now());
        log.debug("即将向数据库写入修改的菜单，参数：{}", menuAddNewDTO);
        if (menuMapper.updateById(menuAddNewDTO) <= 0) {
            throw new ServiceException(ServiceCode.ERR_UPDATE, "修改菜单失败，请稍后再试！");
        }

        // 删除redis中的key
        deleteRedisByKey();
    }



    /**
     * 修改：更换父级权限
     */
    private void isViolation(MenuAddNewDTO menuAddNewDTO) {
        LocalDateTime now = LocalDateTime.now();
        // 更换为一级菜单
        if (menuAddNewDTO.getParentId() == 0) {
            menuAddNewDTO.setDepth(1);
            menuAddNewDTO.setGmtModified(now);

            // 获取当前权限的所有状态子级
            MenuAddNewDTO[] subPermissions = menuMapper.getSubPermissionsWhole(menuAddNewDTO.getId());
            if (subPermissions.length > 0) {
                for (MenuAddNewDTO menuVO2 : subPermissions) {
                    menuVO2.setDepth(menuAddNewDTO.getDepth() + 1);
                    menuVO2.setGmtModified(now);
                    menuVO2.setSort(menuAddNewDTO.getSort());

                    log.debug("【" + menuAddNewDTO.getName() + "】子级更新为二级菜单，操作中...");
                    if (menuMapper.updateById(menuVO2) <= 0) {
                        throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "【" + menuAddNewDTO.getName() + "】子级更新为二级菜单失败");
                    }
                }
            }
            log.debug("【" + menuAddNewDTO.getName() + "】更新为一级菜单，操作中...");
            if (menuMapper.updateById(menuAddNewDTO) <= 0) {
                throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "【" + menuAddNewDTO.getName() + "】更新为一级菜单失败");
            }
            return;
        }


        // 获取需要更换的父级信息
        MenuListVO menuListVO1 = menuMapper.getMenuById(menuAddNewDTO.getParentId());
        int depthParent = menuListVO1.getDepth();       // 获取父级深度

        log.debug("【更换父级】新的父级深度为：{}", depthParent);

        menuAddNewDTO.setDepth(depthParent + 1);          // 重置当前权限深度
        menuAddNewDTO.setSort(menuListVO1.getSort());   // 同步父级的排序
        menuAddNewDTO.setGmtModified(now);              // 重置更新时间
        if (depthParent == 3) {
            throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "无法在三级权限外创建【" + menuAddNewDTO.getName() + "】");
        }


        // 获取当前权限下的所有状态子级
        MenuAddNewDTO[] subPermissions = menuMapper.getSubPermissionsWhole(menuAddNewDTO.getId());
        if (subPermissions.length > 0) {
            if (depthParent == 2) {
                throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "无法在三级权限外创建【" + menuAddNewDTO.getName() + "】的子级");
            }
            for (MenuAddNewDTO menuVO2 : subPermissions) {

                log.debug("【" + menuAddNewDTO.getName() + "】的子级更换为三级菜单，操作中...");

                menuVO2.setDepth(menuAddNewDTO.getDepth() + 1);
                menuVO2.setGmtModified(now);
                menuVO2.setSort(menuAddNewDTO.getSort());

                if (menuMapper.updateById(menuVO2) <= 0) {
                    throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "【" + menuAddNewDTO.getName() + "】的子级更换为三级菜单失败");
                }
            }
        }
        log.debug("【" + menuAddNewDTO.getName() + "】更换为二级级菜单，操作中...");

        if (menuMapper.updateById(menuAddNewDTO) <= 0) {
            throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "【" + menuAddNewDTO.getName() + "】更换为二级级菜单失败");
        }
    }



    /**
     * 修改：修改权限状态
     *
     * @param menuAddNewDTO
     */
    @Override
    public void switchPermission(MenuAddNewDTO menuAddNewDTO) {
        /**
         * 当前权限删除时需要判断是否存在子级
         */
        int status = menuAddNewDTO.getStatus();
        Long menuId = menuAddNewDTO.getId();      // 当前权限id
        Long parentId = menuAddNewDTO.getParentId();    // 当前权限父级id

        if (status == 0) {
            switchPermissionDisable(menuId, status);
            return;
        }


        // 启用，当前权限为一级权限，没有父级
        if (parentId == 0) {
            menuMapper.updateStatusById(menuId, status);
            MenuAddNewDTO[] subPermissions = menuMapper.getSubPermissionsWhole(menuId);
            if (subPermissions.length > 0) {
                for (int i = 0; i < subPermissions.length; i++) {
                    menuMapper.updateStatusById(subPermissions[i].getId(), status);

                    MenuAddNewDTO[] subPermissionsTWO = menuMapper.getSubPermissionsWhole(subPermissions[i].getId());
                    if (subPermissionsTWO.length > 0) {
                        for (int j = 0; j < subPermissionsTWO.length; j++) {
                            menuMapper.updateStatusById(subPermissionsTWO[j].getId(), status);
                        }
                    }
                }
            }
            return;
        }

        // 启用,存在父级
        switchPermissionStart(menuId, parentId, status, menuAddNewDTO.getName());
    }

    // 单独封装禁用状态
    private void switchPermissionDisable(Long menuId, Integer status) {

        MenuAddNewDTO[] subPermissions = menuMapper.getSubPermissionsWhole(menuId);
        if (subPermissions.length > 0){
            for (MenuAddNewDTO menuAddNewDTO : subPermissions) {
                // 修改三级权限状态
                log.error("二级菜单");
                MenuAddNewDTO[] subPermissionsTWO = menuMapper.getSubPermissionsWhole(menuAddNewDTO.getId());
                if (subPermissionsTWO.length>0){
                    for (MenuAddNewDTO menuAddNewDTO2 : subPermissionsTWO) {
                        if (menuMapper.updateStatusById(menuAddNewDTO2.getId(), status)<=0){
                            throw new ServiceException(ServiceCode.ERR_CONFLICT,"修改状态失败，联系管理员");
                        }
                    }
                }
                // 修改二级权限状态
                if (menuMapper.updateStatusById(menuAddNewDTO.getId(), status)<=0){
                    throw new ServiceException(ServiceCode.ERR_CONFLICT,"修改状态失败，联系管理员");
                }
            }
        }
        // 修改当前权限状态
        menuMapper.updateStatusById(menuId, status);
    }

    // 单独封装启用状态
    private void switchPermissionStart(Long menuId, Long parentId, Integer status, String name) {
        MenuListVO parentMenu = menuMapper.getMenuById(parentId); // 获取父级状态
        if (parentMenu != null) {
            if (parentMenu.getStatus() == 0) {
                throw new ServiceException(ServiceCode.ERR_PARENT_DISABLED, "【" + name + "】的父级已被禁用，请先启用【" + menuMapper.getNameById(parentId) + "】");
            }
            if (parentMenu.getDepth() == 3) {
                throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "无法在三级权限下启用【" + name + "】，请重新选择父级");
            }

            log.error("准备启用");
            // 启用
            int a = menuMapper.updateStatusById(menuId, status);
            if (a == 1) {
                log.error("父级已启用");
            }
            MenuAddNewDTO[] subPermissions = menuMapper.getSubPermissionsWhole(menuId);
            if (subPermissions.length > 0) {
                log.error("准备启用子级");
                if (parentMenu.getDepth() == 2) {
                    throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "【" + name + "】无法在三级权限下启用子权限，请重新选择父级");
                }
                log.error("子级个数：{}", subPermissions.length);
                for (int i = 0; i < subPermissions.length; i++) {
                    menuMapper.updateStatusById(subPermissions[i].getId(), status);
                }
            }
        } else {
            throw new ServiceException(ServiceCode.ERR_PARENT_NON_EXISTENT, "启用状态时发现父级已不存在");
        }
    }


    /**
     * 判断：根据id判断当前权限是否存在子级权限
     */
    @Override
    public void isSonMenuById(Long id) {
        if (menuMapper.isSonMenuById(id) <= 0) {
            throw new ServiceException(ServiceCode.ERR_INSERT, "当前权限没有子权限");
        }
    }


    @Override
    public List<MenuListVO> listByKeyword(MenuListVO menuListVO) {
        log.debug("准备开始处理条件分页查询菜单的业务，参数：{}", menuListVO);
        String start = menuListVO.getUpdateTimeStart();
        String end = menuListVO.getUpdateTimeEnd();
        if (start != null && end != null) {
            if (start.trim().length() > 0 && end.trim().length() > 0) {
                menuListVO.setUpdateTimeStart(DateUtils.getLDTimeTOStr(start));
                menuListVO.setUpdateTimeEnd(DateUtils.getLDTimeTOStr(end));
            }
        }
        return menuMapper.listByKeyword(menuListVO);
    }



    @Override
    public int getCountAll(MenuListVO menuListVO) {
        log.debug("准备开始处理条件查询结果集数量的业务，参数：{}", menuListVO);
        return menuMapper.getCountAll(menuListVO);
    }


    @Override
    public MenuListVO getBackFillById(Long id) {
        log.debug("准备开始处理数据回填的业务，参数：{}", id);
        return menuMapper.getBackFillById(id);
    }


    /**
     * 首页查询分类树
     * @return
     */
    @Override
    public MenusTreeVO categoryTree() {
        if (redisTemplate.hasKey(Menu_TREE_KEY)) {
            log.debug("即将向redis中查询权限树..");
            MenusTreeVO<MenuTreeEntity> treeVO =
                    (MenusTreeVO<MenuTreeEntity>) redisTemplate
                            .boundValueOps(Menu_TREE_KEY).get();
            return treeVO;
        }

        System.out.println("redis中无数据，即将查询数据库【查询权限树】");
        List<MenuListVO> menuStandardVOs = menuMapper.menuTreeList();

        MenusTreeVO<MenuTreeEntity> treeVO = initTree(menuStandardVOs);

        redisTemplate.boundValueOps(Menu_TREE_KEY).set(treeVO, 24, TimeUnit.HOURS);

        return treeVO;
    }


    /**
     * 首页查询分类树--单独封装一部分逻辑
     * @param menuStandardVOs
     * @return
     */
    private MenusTreeVO<MenuTreeEntity> initTree(List<MenuListVO> menuStandardVOs) {

        // 确定所有分类对象的父分类
        Map<Long, List<MenuTreeEntity>> map = new HashMap<>();
        log.info("当前分类对象总数为:{}", menuStandardVOs.size());
        for (MenuListVO menuStandardVO : menuStandardVOs) {

            MenuTreeEntity menuTreeEntity = new MenuTreeEntity();
            BeanUtils.copyProperties(menuStandardVO, menuTreeEntity);
            Long parentId = menuTreeEntity.getParentId();

            if (map.containsKey(parentId)) {
                //如果key存在,将当前分类对象添加到map的value的List中
                map.get(parentId).add(menuTreeEntity);
            } else {
                // 不存在则创建List对象,将分类对象保存在这个List中
                List<MenuTreeEntity> value = new ArrayList<>();
                value.add(menuTreeEntity);
                map.put(parentId, value);
            }
        }

        List<MenuTreeEntity> firstLevels = map.get(0L);
        // 判断firstLevels是否为空
        if (firstLevels == null) {
            throw new CoolSharkServiceException(
                    ResponseCode.INTERNAL_SERVER_ERROR, "当前项目没有根分类");
        }
        //遍历所有一级分类对象
        for (MenuTreeEntity oneLevel : firstLevels) {

            Long secondLevelParentId = oneLevel.getId();
            List<MenuTreeEntity> secondLevels = map.get(secondLevelParentId);

            if (secondLevels == null) {
                log.warn("id为【{}】的权限没有二级级分类内容", secondLevelParentId);
                continue;
            }
            // 遍历二级分类集合
            for (MenuTreeEntity twoLevel : secondLevels) {

                Long thirdLevelParentId = twoLevel.getId();
                List<MenuTreeEntity> thirdLevels = map.get(thirdLevelParentId);

                if (thirdLevels == null) {
                    log.warn("id为【{}】的权限没有三级分类内容", thirdLevelParentId);
                    continue;
                }

                // 将三级分类集合添加到二级分类对象的children属性中
                twoLevel.setChildrens(thirdLevels);
            }
            // 将二级分类对象集合添加到一级分类对象的children属性中
            oneLevel.setChildrens(secondLevels);
        }
        MenusTreeVO<MenuTreeEntity> treeVO = new MenusTreeVO<>();
        // 将一级分类的集合赋值给该对象的属性
        treeVO.setCategories(firstLevels);

        return treeVO;
    }



    // 判断更换后当前权限及其子级是否会超越三级菜单的定义
    @Override
    public void isViolationMenuTree(Long menuId, Long parentId) {

        // 获取需要更换的父级信息
        MenuListVO menuListVO = menuMapper.getMenuById(parentId);
        if (menuListVO == null) {
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, "需要更换的父级不存在！");
        }
        int depthParent = menuListVO.getDepth();
        log.debug("【更换父级】新的父级深度为：{}", depthParent);
        if (depthParent == 3) {
            throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "无法在三级权限下创建【"+menuListVO.getName()+"】");
        }

        // 判断当前权限是否包含子级
        Long[] subPermissions = menuMapper.getSubPermissions(menuId);
        if (subPermissions.length > 0) {
            if (depthParent == 2) {
                throw new ServiceException(ServiceCode.ERR_Menu_Violation_Tree, "【"+menuListVO.getName()+"】的子级无法在三级权限下生成子权限");
            }
        }
    }


    // 删除redis中指定的Key
    private void deleteRedisByKey() {
        if (redisTemplate.hasKey(Menu_TREE_KEY)) {
            redisTemplate.delete(Menu_TREE_KEY);
        }
    }
}
